package com.xcity.game.combat;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xcity.db.entity.Player;
import com.xcity.game.combat.listener.CombatListener;
import com.xcity.game.combat.listener.combat.CombatListenerFactory;
import com.xcity.game.combat.listener.combat.PVPCombatListenerV2;
import com.xcity.game.combat.unit.CombatUnit;
import com.xcity.game.common.Cause;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.msg.CombatRoundPush;
import com.xcity.pomelo.request.CombatActionRequest;
import com.xcity.pomelo.request.CombatArenaOfflineRequest;
import com.xcity.pomelo.request.CombatEndRequest;
import com.xcity.pomelo.request.CombatInfoRequest;
import com.xcity.pomelo.request.CombatMineAttackRequest;
import com.xcity.pomelo.request.CombatMineTakeRequest;
import com.xcity.pomelo.request.CombatNextRoundRequest;
import com.xcity.pomelo.request.CombatPVERequest;
import com.xcity.pomelo.request.CombatPVPRequest;
import com.xcity.pomelo.request.CombatPeakRoadRequest;
import com.xcity.pomelo.request.CombatResStageRequest;
import com.xcity.pomelo.request.CombatWorldBossRequest;
import com.xcity.pomelo.response.CombatResponse;
import com.xcity.pomelo.response.CombatRoundResponse;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.Response;

import naga.x.App;
import naga.x.common.Updatable;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.PomeloPacket;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;

@OPHandler
public class CombatService implements Service, Updatable, EventListener {

	public static final Logger LOG = LoggerFactory.getLogger(CombatService.class);
	
	// 回合超时时间（如果客户端长时间未请求下一回合数据，则服务器主动推送下一回合数据）
	public static final long ROUND_TIME_OUT_MILLIS = 90000;
	
	private Map<Long, CombatDecorator> combats = new ConcurrentHashMap<>(128);
	
	@Override
	public void startup() throws ServiceException {
		App.getApp().getEventManager().register(this);
		App.getApp().getUpdater().addAsync(this);
//		CombatBuffTemplate.clear();
	}

	@Override
	public void shutdown() throws ServiceException {
		Collection<CombatDecorator> vals = combats.values();
		for (CombatDecorator decorator : vals) {
			decorator.combat.end(Cause.SHUTDOWN);
		}
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	@Override
	public boolean update() {
		Iterator<CombatDecorator> itr = combats.values().iterator();
		while (itr.hasNext()) {
			CombatDecorator decorator = itr.next();
			if (decorator.combat.isTimeout() // avoid foreach unit state
					&& decorator.combat.isEnd()) { // process the combat state
				// 限时战斗超时处理
				itr.remove();
				decorator.combat.end(Cause.TIME_OUT);
			} else if (decorator.isTimeout()) {
				// 回合超时
				Round r = nextRound(decorator, Cause.TIME_OUT);
				if (r != null) {
					decorator.player.send(new CombatRoundPush(r));
				} else {
					itr.remove();
				}
			}
		}
		PVPCombatListenerV2.update();
		return true;
	}
	
	@Override
	public int[] getEventTypes() {
		return new int[] { GameEvents.EVENT_PLAYER_LOGOUT };
	}

	@Override
	public void handleEvent(Event event) {
		switch (event.getType()) {
			case GameEvents.EVENT_PLAYER_LOGOUT:
				playerLogout(event.getParameter());
				break;
		}
	}
	
	protected void playerLogout(Player player) {
		CombatDecorator decorator = combats.remove(player.getId());
		if (decorator != null) {
			decorator.combat.end(Cause.LOGOUT);
		}
	}
	
	public Combat getCombat(Player player) {
		CombatDecorator decorator = combats.get(player.getId());
		return decorator != null ? decorator.combat : null;
	}
	
	/**
	 * 请求战斗信息（准备战斗前）
	 * @param session
	 * @param req
	 */
	@PomeloOP(PomeloRoute.COMBAT_INFO)
	protected void requestCombatInfo(Session session, CombatInfoRequest req) {
		CombatListener listener = CombatListenerFactory.create(session.getClient(), req.getType());
		if (listener == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		try {
			listener.prepareCombat(session, req);
		} catch (CombatException e) {
			session.send(new HintResponse(req.getSerial(), e.getMessage()));
		}
	}
	
	@PomeloOP(PomeloRoute.COMBAT_END)
	protected void requestEnd(Session session, CombatEndRequest req) {
		Player player = session.getClient();
		CombatDecorator decorator = combats.remove(player.getId());
		if (decorator == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.COMBAT_NOT_FOUND));
			return;
		}
		decorator.combat.state = Combat.State.TARGET_WON; // force update state
		decorator.combat.end(Cause.QUIT);
		session.send(new Response(req.getSerial()));
	}
	
	/**
	 * 开始一场新的战斗
	 * @param session
	 */
	@PomeloOP(PomeloRoute.COMBAT_PVP)
	protected void createPVP(Session session, CombatPVPRequest req) {
		createCombat(session, req, Combat.Type.PVP);
	}
	
	@PomeloOP(PomeloRoute.COMBAT_PVE)
	protected void createPVE(Session session, CombatPVERequest req) {
		createCombat(session, req, Combat.Type.PVE);
	}
	
	@PomeloOP(PomeloRoute.COMBAT_ARENA)
	protected void createArenaCombat(Session session, CombatArenaOfflineRequest req) {
		createCombat(session, req, Combat.Type.ARENA_OFFLINE);
	}
	
	@PomeloOP(PomeloRoute.COMBAT_RESOURCE_INSTANCE)
	protected void createResStageCombat(Session session, CombatResStageRequest req) {
		createCombat(session, req, Combat.Type.RESOURCE_INSTANCE);
	}
	@PomeloOP(PomeloRoute.COMBAT_PEAK_ROAD)
	protected void createPeakRoadCombat(Session session, CombatPeakRoadRequest req) {
		createCombat(session, req, Combat.Type.PEAK_ROAD);
	}
	
	@PomeloOP(PomeloRoute.COMBAT_MINE_TAKE)
	protected void createMineTakeCombat(Session session, CombatMineTakeRequest req) {
		createCombat(session, req, Combat.Type.MINE_TAKE);
	}
	
	@PomeloOP(PomeloRoute.COMBAT_MINE_ATTACK)
	protected void createMineAttackCombat(Session session, CombatMineAttackRequest req) {
		createCombat(session, req, Combat.Type.MINE_ATTACK);
	}
	
	@PomeloOP(PomeloRoute.COMBAT_WORLD_BOSS)
	protected void createWorldBossCombat(Session session, CombatWorldBossRequest req) {
		createCombat(session, req, Combat.Type.WORLD_BOSS);
	}
	
	protected void createCombat(Session session, PomeloPacket packet, Combat.Type type) {
		try {
			Player player = session.getClient();
			CombatDecorator decorator = combats.remove(player.getId());
			if (decorator != null) {
//				throw new CombatException(MessageConst.COMBAT_NOT_OVER);
				decorator.combat.end(Cause.TIME_OUT, false);
			}
			CombatResponse res = CombatListenerFactory.create(player, type).create(this, type, session, packet);
			combats.put(player.getId(), new CombatDecorator(res.combat, player));
			session.send(res);
		} catch (CombatException e) {
			session.send(new HintResponse(packet.getSerial(), e.getMessage()));
		}
	}
	
	@PomeloOP(PomeloRoute.COMBAT_ROUND)
	protected void requestNextRound(Session session, CombatNextRoundRequest req) {
		if (req.getRound() < 0) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		Player player = session.getClient();
		CombatDecorator decorator = combats.get(player.getId());
		if (decorator == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.COMBAT_NOT_FOUND));
			return;
		}
//		if (req.getRound() != 0 && !decorator.combat.isEnd()) { // 除了第一回合、最后一回合外，都需限制请求频率
//			int n = CombatUtils.getAliveUnitCount(decorator.combat);
//			if (decorator.lastRoundStartTimestamp + n * 800 > TimeUpdater.getInstance().now()) {
//				session.send(new HintResponse(req.getSerial(), MessageConst.OPERATE_TOO_FREQUENT));
//				return;
//			}
//		}
		Round round = getRound(decorator, req.getRound(), Cause.MANUAL);
		if (round != null) {
			session.send(new CombatRoundResponse(req.getSerial(), round, false));
		} else {
			combats.remove(player.getId());
			session.send(new CombatRoundResponse(req.getSerial(), null, true));
		}
	}
	
	@PomeloOP(PomeloRoute.COMBAT_ACTION)
	protected void requestAction(Session session, CombatActionRequest req) {
		Player player = session.getClient();
		CombatDecorator decorator = combats.get(player.getId());
		if (decorator == null) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.COMBAT_NOT_FOUND));
			session.send(new Response(req.getSerial()));
			return;
		}
		Combat combat = decorator.combat;
		CombatUnit unit = combat.find(req.getId() == 0L ? player.getId() : req.getId());
		if (unit == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.COMBAT_NOT_JOIN));
//			session.send(new Response(req.getSerial()));
			return;
		}
//		if (unit.getTeam().getLeader().getId() != player.getId()) {
//			session.send(new Response(req.getSerial()));
//			return;
//		}
//		if (unit.isDied()) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.COMBAT_UNIT_DIED));
//			return;
//		}
		Round round = combat.getLastRound();
		if (round == null || req.getIndex() < 0 || req.getIndex() >= round.actions.size()) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		if (req.getIndex() < round.actionIndex) {
//			session.send(new Response(req.getSerial()));
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		round.useSkill(combat, req.getIndex());
//		unit.addMp(req.getMp() - unit.getMp()); // sync mp
//		combat.addAction(req.isUseSkill());
		session.send(new Response(req.getSerial()));
		session.send(new CombatRoundPush(round));
//		session.send(new CombatRoundResponse(req.getSerial(), round));
	}
	
	private static Round nextRound(CombatDecorator decorator, String cause) {
		return getRound(decorator, decorator.combat.roundCount(), cause);
	}
	
	private static Round getRound(CombatDecorator decorator, int roundIndex, String cause) {
		Combat combat = decorator.combat;
		if (combat.isEnd()) {
			combat.end(cause);
			return null;
		}
		Round round = combat.getRound(roundIndex);
		if (round == null) {
			combat.end(cause);
			return null;
		}
		if (roundIndex == combat.roundCount() - 1) {
			decorator.lastRoundStartTimestamp = TimeUpdater.getInstance().now();
		}
		return round;
	}
	
	public static class CombatDecorator {
		
		Combat combat;
		Player player;
		long lastRoundStartTimestamp; // 上一次客户端请求回合数据时间
		
		CombatDecorator(Combat combat, Player player) {
			this.combat = combat;
			this.player = player;
			this.lastRoundStartTimestamp = TimeUpdater.getInstance().now();
		}
		
		boolean isTimeout() {
			return lastRoundStartTimestamp + CombatService.ROUND_TIME_OUT_MILLIS < TimeUpdater.getInstance().now();
		}
	}


}
